package fr.ups.jim.rdf.tp5;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;

public class ShannonGame {

	public static void main(String[] args) throws IOException,
			FileNotFoundException {

		String fileName = "en_tatoeba.corpus";
		// String fileName = "corpus_abc.dat";

		InputStream ips = new FileInputStream(fileName);
		InputStreamReader ipsr = new InputStreamReader(ips, "UTF-8");
		BufferedReader br = new BufferedReader(ipsr);

		// markov order here is 2
		ProbabilityTable pt = new ProbabilityTable(2);

		// obtain the frequency table
		while (true) {
			String line = br.readLine();
			if (line == null)
				break;

			pt.addOneLine(line);
		}

		// change the frequency table to probability table
		pt.freqToProbability();

		// output the results
		pt.outputProbabilities();
	}

}

class ProbabilityTable {
	private Map<String, HashMap<Character, Double>> pTable;
	private int markovOrder;
	private static final String PHRASE_START = "<s>";

	public ProbabilityTable(int markovOrder) {
		this.markovOrder = markovOrder;
		pTable = new HashMap<String, HashMap<Character, Double>>();
	}

	/**
	 * Count the number of apperance of a character after specific letter
	 * combination, and update its frequency in the stored probability table
	 * 
	 * @param lettersPre
	 *            the letters combination before the letter to be counted
	 * @param letterCurrent
	 *            the letter to be counted
	 */
	private void count(String lettersPre, char letterCurrent) {
		if (pTable.containsKey(lettersPre)) {
			if (pTable.get(lettersPre).containsKey(letterCurrent)) {
				double value = pTable.get(lettersPre).get(letterCurrent);
				pTable.get(lettersPre).put(letterCurrent, value + 1.0);
			} else
				pTable.get(lettersPre).put(letterCurrent, 1.0);
		} else {
			HashMap<Character, Double> newCombination = new HashMap<Character, Double>();
			newCombination.put(new Character(letterCurrent), 1.0);
			pTable.put(lettersPre, newCombination);
		}
	}

	/**
	 * Obtain the probability for a given Character after specific letter(s)
	 * from the stored Probability Table
	 * 
	 * @param lettersPre
	 *            letters has appeared
	 * @param letterCurrent
	 *            letter to be predicted for the probability
	 * @return the probability
	 */
	public double get(String lettersPre, char letterCurrent) {
		if (pTable.containsKey(lettersPre)) {
			if (pTable.get(lettersPre)
					.containsKey(new Character(letterCurrent)))
				return pTable.get(lettersPre).get(new Character(letterCurrent));
			else
				return 0.0;
		} else
			return 0.0;
	}

	/**
	 * This method count the frequency after specific letters combination for
	 * each character in a given string
	 * 
	 * @param line
	 *            the given string
	 */
	public void addOneLine(String line) {
		for (int i = 0; i < markovOrder - 1; i++) {
			StringBuffer preLetters = new StringBuffer();
			for (int j = i + 1; j < markovOrder; j++) {
				preLetters.append(PHRASE_START);
			}
			if (i > 0)
				preLetters.append(line.substring(0, i));
			// System.out.println(preLetters.append(line.charAt(i)).toString());
			count(preLetters.toString(), line.charAt(i));
		}

		for (int i = markovOrder - 1; i < line.length(); i++) {
			StringBuffer preLetters = new StringBuffer();

			for (int j = i + 1 - markovOrder; j < i; j++)
				preLetters.append(line.charAt(j));
			// System.out.println(preLetters.append(line.charAt(i)).toString());
			count(preLetters.toString(), line.charAt(i));
		}

	}

	/**
	 * Calculate the probability according to its stored table
	 */
	public void freqToProbability() {
		Object[] letterCombination = (Object[]) pTable.keySet().toArray();
		for (Object lc : letterCombination) {
			HashMap<Character, Double> line = pTable.get(lc.toString());
			double sum = 0.0;
			double freq;
			Object[] letters = (Object[]) line.keySet().toArray();
			for (Object c : letters) {
				sum += line.get((Character) c).doubleValue();
			}
			for (Object c : letters) {
				freq = line.get((Character) c).doubleValue();
				line.put((Character) c, freq / sum);
			}
		}
	}

	/**
	 * Output the probability table to the console
	 */
	public void outputProbabilities() {
		System.out.println("distrib = {");
		Object[] keySet = (Object[]) pTable.keySet().toArray();
		for (Object key : keySet) {
			HashMap<Character, Double> temp = pTable.get(key.toString());
			Object[] charSet = (Object[]) temp.keySet().toArray();

			System.out.print("'" + key.toString() + "' : {");
			for (Object c : charSet)
				System.out.print("'" + (Character) c + "': "
						+ temp.get((Character) c).doubleValue() + ", ");
			System.out.print(" } \n");
		}

		System.out.println("}");
	}
}
